/*
    This file is part of QTau
    Copyright (C) 2013-2018  Tobias "Tomoko" Platen <tplaten@posteo.de>
    Copyright (C) 2013       digited       <https://github.com/digited>
    Copyright (C) 2010-2013  HAL@ShurabaP  <https://github.com/haruneko>

    QTau is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    SPDX-License-Identifier: GPL-3.0+
*/

#define __devloglevel__ 4

#include "ui/dynDrawer.h"
#include "ui/Config.h"
#include "ui/noteEditor.h"


// FIXME dynamics not supported yet, need synth/storeage support
// scalars: VEL,

#include <qevent.h>
#include <QPainter>
#include <QPixmap>
#include "assert.h"
#include "tempomap.h"

#include <QDebug>
#include <QTextEdit>
#include <QVBoxLayout>

// TODO: this class is a dummy

qtauDynLabel::qtauDynLabel(const QString &txt, QWidget *parent)
    : QLabel(txt, parent), _state(off) {}
qtauDynLabel::~qtauDynLabel() {}

void qtauDynLabel::mousePressEvent(QMouseEvent *event) {
  if (event->button() == Qt::LeftButton)
    emit leftClicked();
  else if (event->button() == Qt::RightButton)
    emit rightClicked();
}

qtauDynLabel::EState qtauDynLabel::state() { return _state; }
void qtauDynLabel::setState(EState s) { _state = s; }

//===========================================================================

#define SLIDE 5
#define WIDTH 10

qtauDynDrawer::qtauDynDrawer(qtauNoteEditor *qne, QWidget *parent)
    : QWidget(parent), _offset(0), _bgCache(nullptr) {
  _editor = qne;
  connect(_editor, &qtauNoteEditor::repaintDynDrawer, this,
          &qtauDynDrawer::onRepaintDynDrawer);
}

qtauDynDrawer::~qtauDynDrawer() {
  //
}

void qtauDynDrawer::onRepaintDynDrawer(void) { update(); }

void qtauDynDrawer::setOffset(int off) {
  if (off != _offset) {
    _offset = off;
    update();
  }
}

void qtauDynDrawer::configure(const SNoteSetup &newSetup) {
  _ns = newSetup;
  updateCache();
  update();
}

//------------------------------------

void qtauDynDrawer::paintEvent(QPaintEvent *event) {
  // draw bg
  int hSt = event->rect().x() + _offset;
  _hSt = hSt;
  int barWidth = _ns.note.width() * 4;
  int firstBar = hSt / barWidth;
  int cacheOffset = hSt - firstBar * barWidth;

  QRect screenRect(event->rect());
  QRect cacheRect(screenRect);
  cacheRect.moveLeft(cacheRect.x() + cacheOffset);

  QPainter p(this);
  p.drawPixmap(screenRect, *_bgCache, cacheRect);

  if (_velSelected)  // draw vel
  {
    _h = event->rect().height();
    foreach (quint64 key, _editor->_notes.idMap.keys()) {
      qne::editorNote &n = _editor->_notes.idMap[key];

      double pulsesToPixels = (double)_ns.note.width() / c_midi_ppq;
      int notePos = n.pulseOffset * pulsesToPixels;

      notePos -= SLIDE;
      notePos -= hSt;

      float velocity = 100 / 128.0;

      if (key == _noteUnderCursor) velocity = _velocity;

      int width = WIDTH;
      int height = _h * (1 - velocity);
      QRect noteRect(notePos, _h, width, height - _h);

      if (key == _noteUnderCursor)
        p.fillRect(noteRect, QBrush(Qt::blue));
      else
        p.fillRect(noteRect, QBrush(Qt::green));
    }
  }
}

void qtauDynDrawer::resizeEvent(QResizeEvent *) { updateCache(); }

// incomplete :: editing needs to be done

void qtauDynDrawer::mouseDoubleClickEvent(QMouseEvent *) {

    _edit = new QTextEdit(this);
   // _edit->setGeometry(r);
    _edit->setVisible(true);
    _edit->setText("TODO");
    _edit->setFocus();
    _edit->selectAll();
    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(_edit);
    setLayout(layout);
}

void qtauDynDrawer::mouseMoveEvent(QMouseEvent *event) {
  (void)event;
#if TODO
  //refactoring needed
 // if (!(_noteUnderCursor !=)) {
    if (event->x() > _notePos && event->x() < _notePos + WIDTH &&
        _velSelected) {
      _velocity = (_h - event->y()) / (1.0 * _h);
  //  }
    update();
  }
#endif
}

void qtauDynDrawer::mousePressEvent(QMouseEvent *event) {
  //
  if (_velSelected)  // draw vel
  {
    foreach (quint64 key, _editor->_notes.idMap.keys()) {
      qne::editorNote &n = _editor->_notes.idMap[key];

      double pulsesToPixels = (double)_ns.note.width() / c_midi_ppq;
      int notePos = n.pulseOffset * pulsesToPixels;

      notePos -= SLIDE;
      notePos -= _hSt;

      if (event->x() > notePos && event->x() < notePos + WIDTH) {
        _noteUnderCursor = key;
        _notePos = notePos;
        update();
      }

      _velocity = (_h - event->y()) / (1.0 * _h);
    }
  }
}

void qtauDynDrawer::mouseReleaseEvent(QMouseEvent *) {
  _noteUnderCursor = -1;
  // if(_velSelected) updateModel()

  update();
}

void qtauDynDrawer::wheelEvent(QWheelEvent *event) {
  if (event->modifiers() & Qt::ControlModifier)
    emit zoomed(event->delta());
  else
    emit scrolled(event->delta());
}

void qtauDynDrawer::updateCache() {
  // DEVLOG_DEBUG("updateCache()");
  if (geometry().height() == 0) {
    DEVLOG_ERROR(
        "Zero height of dynDrawer in updateCache()... This definitely "
        "shouldn't happen.");
    return;
  }

  // bars fully fit in + 2 for partially visible bars
  int requiredCacheWidth =
      _ns.barScreenOffsets[_ns.numBars - 1] + _ns.note.width() * 8;

  if (!_bgCache || (_bgCache->width() < requiredCacheWidth ||
                    _bgCache->height() < geometry().height())) {
    if (_bgCache) delete _bgCache;

    _bgCache = new QPixmap(requiredCacheWidth, geometry().height());
  }

  // prepare painting data
  int vSt = 0;
  int vEnd = geometry().height();
  int mSt = vEnd / 2 - 10;
  int mEnd = mSt + 20;

  int bar = 0;
  int beat = 0;
  int pxOff = 0;

  QVector<QPoint> noteLines;
  QVector<QPoint> barLines;
  assert(_ns.tmap);

  while (true) {
    if (pxOff >= requiredCacheWidth) break;

    fraction time = _ns.tmap->getTimeSignatureForBar(bar);

    if (beat == time.numerator) {
      barLines.append(QPoint(pxOff, vSt));
      barLines.append(QPoint(pxOff, vEnd));
      beat = 1;
      bar++;
      time = _ns.tmap->getTimeSignatureForBar(bar);
    } else {
      noteLines.append(QPoint(pxOff, mSt));
      noteLines.append(QPoint(pxOff, mEnd));
      beat++;
    }

    pxOff += _ns.note.width() * 4.0 / time.denominator;
  }

  // paint 'em!
  _bgCache->fill(Qt::white);
  QPainter p(_bgCache);

  p.setPen(QColor(cdef_color_inner_line));
  p.drawLines(noteLines);

  p.setPen(QColor(cdef_color_outer_line));
  p.drawLines(barLines);
}
